using FileTransfer.Client.Http.Api;
using FileTransfer.Client.Http.Client;
using FileTransfer.Client.Http.Model;
using FileTransfer.Client.Model;
using Microsoft.Extensions.Logging;

namespace FileTransfer.Client.Upload;

public class FileChunkUploader(
    ApiClient api,
    string fileName,
    string fileMd5,
    long totalSize,
    ILoggerFactory? loggerFactory = null
)
{
    private const int MaxRetry = 5;
    private const long FailTimeout = 60000;

    private readonly FileControllerApi _fileApi = api.FileControllerApi;
    private readonly ILogger<FileChunkUploader>? _logger =
        loggerFactory?.CreateLogger<FileChunkUploader>();

    public FileControllerApi FileApi => _fileApi;

    public async Task<bool> UploadFileChunkAsync(
        FileChunk chunk,
        ProgressListenerDelegate? onProgress = null
    )
    {
        var retryCount = 0;
        var startTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();

        while (retryCount < MaxRetry)
        {
            try
            {
                using var ms = new MemoryStream(chunk.ChunkData);
                await using var content = new ProgressableReadStream(
                    ms,
                    chunk.ChunkData.Length,
                    (bytesWritten, contentLength) =>
                    {
                        _logger?.LogInformation(
                            "上传进度：{BytesWritten} / {ContentLength}",
                            bytesWritten,
                            contentLength
                        );
                        onProgress?.Invoke(bytesWritten, contentLength);
                    }
                );
                var response = await _fileApi.UploadChunkStreamAsync(
                    fileMd5,
                    chunk.ChunkMd5,
                    chunk.ChunkIndex,
                    chunk.ChunkOffset,
                    new FileParameter(content)
                );
                var now = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                _logger?.LogInformation(
                    "分片 [{ChunkIndex}] 上传成功，起始位置：{ChunkOffset}，大小：{ChunkSize}，分片 MD5：{ChunkMD5}，耗时：{ElapsedTime}ms 返回：{Result}",
                    chunk.ChunkIndex,
                    chunk.ChunkOffset,
                    chunk.ChunkData.Length,
                    chunk.ChunkMd5,
                    now - startTime,
                    response
                );

                return true;
            }
            catch (Exception e)
            {
                retryCount++;
                _logger?.LogError(
                    e,
                    "上传分片失败，重试次数: {RetryCount}, 错误信息: {ErrorMessage}",
                    retryCount,
                    e.Message
                );
                await Task.Delay(2000);
            }
        }

        _logger?.LogError("上传分片{ChunkIndex}失败，超过最大重试次数", chunk.ChunkIndex);
        return false;
    }

    public async Task StartAsync()
    {
        await _fileApi.NewUploadAsync(
            fileMd5,
            new FileUploadInfo { FileName = fileName, TotalSize = totalSize }
        );
    }

    public async Task<string> CompleteAsync()
    {
        return await _fileApi.CompleteUploadAsync(fileMd5);
    }

    public void Cancel()
    {
        // Implementation for cancellation if needed
    }
}
